// <copyright file="LinearRing.cs" company="Signal Processing Lab, Wuhan University">
// Copyright (c) 2009-2010 All Right Reserved
// </copyright>
// <author>Xinping Deng</author>
// <email>dentoby@gmail.com</email>
// <date>2010-4</date>
// <summary>This is part of Image.NET project.</summary>

namespace SPLWHU.Core.Vectors
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// The summary of LinearRing.
    /// </summary>
    public class LinearRing : LineString
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LinearRing"/> class.
        /// </summary>
        public LinearRing()
            : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearRing"/> class by specifying a list of points.
        /// </summary>
        /// <param name="points">The points.</param>
        public LinearRing(List<Point2d> points)
            : base(points)
        {
            if (this.EndPoint != this.StartPoint)
            {
                this.AddPoint(new Point2d(this.StartPoint));
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearRing"/> class by specifying an array of points.
        /// </summary>
        /// <param name="points">The points.</param>
        public LinearRing(params Point2d[] points)
            : base(points)
        {
            if (this.EndPoint != this.StartPoint)
            {
                this.AddPoint(new Point2d(this.StartPoint));
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LinearRing"/> class by copying another <see cref="LinearRing"/>.
        /// </summary>
        /// <param name="line">The line to be copyed.</param>
        public LinearRing(LinearRing line)
            : base(line)
        {
        }

        /// <summary>
        /// Determines whether this instance contain the specified point or not.
        /// </summary>
        /// <param name="point">The point to be tested.</param>
        /// <returns>
        /// <c>true</c> if contains the specified point; otherwise, <c>false</c>.
        /// </returns>
        public bool ContainsPoint(Point2d point)
        {
            int numPts = this.PointCount;

            // not a valid polygon
            if (numPts < 3)
            {
                return false;
            }

            int counter = 0;
            Point2d s1 = point, p1 = this[0], p2;

            for (int i = 1; i < numPts; i++)
            {
                p2 = this[i % numPts];

                // the testing point is in in internal [p1.Y, p2.Y].
                if (s1.Y > Math.Min(p1.Y, p2.Y) && s1.Y <= Math.Max(p1.Y, p2.Y))
                {
                    // the tesing point is on the left side of the line [p1, p2].
                    if (s1.X <= Math.Max(p1.X, p2.X))
                    {
                        if (p1.Y != p2.Y)
                        {
                            double xinters = ((s1.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y)) + p1.X;
                            if ((p1.X == p2.X) || (s1.X <= xinters))
                            {
                                counter++;
                            }
                        }  // p1.y != p2.y
                    }
                }

                p1 = p2;
            } // for loop

            if ((counter % 2) == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}
